/* TCSS 590A - Cybernetics and Systems
 * Group Project - Modeling
 * By Barry Jones, Naren Meadem, Michael Pitts, Yuan Tao
 * Fall 2012, University of Washington, Tacoma
 */
package model.delayed.tactical;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;

import output.OutPut;

import model.delayed.Event;
import model.delayed.ModelController;

/**
 * This Controller can work with NegotiableGenerator to try and increase wages 
 * and/or decrease expenses.
 * @author Michael Pitts
 * @version Dec 4, 2012
 */
public class TacticalController implements ModelController
{
	/** Stores the wage Generator.  If null, wages not negotiable.*/
	private final NegotiableGenerator my_wage;
	
	/** Stores the negotiable functionality for wages.*/
	private final ShouldNegotiate my_wage_neg;
	
	/** Stores the expense Generator.  If null, expenses not negotiable.*/
	private final NegotiableGenerator my_expense;
	
	/** Stores the negotiable functionality for expenses.*/
	private final ShouldNegotiate my_expense_neg;
	
	/** The OutPut object, may be null.*/
	private final OutPut my_out;
	
	/**
	 * Generates a new TacticalController, which will negotiate with the given 
	 * wage and expense Generators, if it is also given a corresponding 
	 * ShouldNegotiate object.
	 * @param the_wage is the wage Generator, which may be null.
	 * @param the_wage_neg provides the negotiating functionality for wages, it 
	 * should be null if the_wage is null and should not otherwise.
	 * @param the_expenses is the expense Generator, which may be null.
	 * @param the_expense_neg provides the negotiating functionality for expenses, 
	 * it should be bull if the_expenses is null and should not otherwise.
	 */
	public TacticalController(final NegotiableGenerator the_wage, 
			final ShouldNegotiate the_wage_neg, 
			final NegotiableGenerator the_expenses,
			final ShouldNegotiate the_expense_neg,
			final OutPut the_output)
	{
		my_wage = the_wage;
		my_wage_neg = the_wage_neg;
		my_expense = the_expenses;
		my_expense_neg = the_expense_neg;
		my_out = the_output;
	}
	
	@Override public Collection<Event> compute(final long the_time,
			final Collection<Event> the_events)
	{
		if (my_wage != null && my_wage_neg!= null && my_expense == null) 
			negotiate(my_wage_neg.negotiate(the_time, the_events), the_events);
		else if (my_expense != null && my_expense_neg!= null && my_wage == null) 
			negotiate(my_expense_neg.negotiate(the_time, the_events), the_events);
		else 
			tweakAll(the_time, the_events);
		return the_events;
	}

	/**
	 * Negotiates for both expenses and wages.
	 * @param the_time is the current time.
	 * @param the_events are the Events over which to negotiate.
	 */
	private void tweakAll(final long the_time, final Collection<Event> the_events)
	{
		final Map<Event, Long> map = my_wage_neg.negotiate(the_time, the_events);
		map.putAll(my_expense_neg.negotiate(the_time, the_events));
		negotiate(map, the_events);
	}
	
	/**
	 * Runs negotiations for the Events indicated.
	 * @param the_map maps Events to their negotiable values.
	 * @param the_events is the Collection of Events to be possibly changed.
	 */
	private void negotiate(final Map<Event, Long> the_map, 
			final Collection<Event> the_events)
	{
		Long value;
		Event temp;
		final Collection<Event> events = new ArrayList<Event>(the_events);
		for (Event e : events)
		{
			value = the_map.get(e);
			if (value != null)
			{
				if(e.value > 0 && my_wage.negotiate(e, value))
				{
					temp = new Event(e.value + value, e.time);
					the_events.remove(e);
					the_events.add(temp);
					if (my_out != null)
					{
						my_out.out("Negotiated a bonus of " + temp.value + 
								" from " + e.value);
						my_out.newLine();
					}
				} else if (e.value < 0 && my_expense.negotiate(e, value))
				{
					temp = new Event(e.value + value, e.time);
					the_events.remove(e);
					if (temp.value != 0) the_events.add(temp);
					if (my_out != null)
					{
						my_out.out("Negotiated a reduction of " + temp.value + 
								" from " + e.value);
						my_out.newLine();
					}
				}
			}
		}
	}
}
